//Program by:
//Ben Schmidt - bjs778 - 11054018
//Mike Nimeck - mfn028 - 11053045
//Jeff Huang - jlh105 - 11050226
//
//Fully implemented by hand, but OpenGL SuperBible example
//used heavily as reference

//includes
#include <stdlib.h>
#include <time.h>

#include <GLTools.h>
#include <GLMatrixStack.h>
#include <GLFrame.h>
#include <GLFrustum.h>
#include <GLGeometryTransform.h>
#include <StopWatch.h>
#include <GL/glut.h>


#include "main.h"
#include "flocking.h"

//initialize the flocking particle system
void initFlocking(){  
    //set flock position to the center at the start
    flockingX = WINDOW_WIDTH/2;
    flockingY = WINDOW_HEIGHT/2;
    
    //allocate memory for the position data
    //multiply by 3 for x, y, z
    float* positions = new float[FLOCK_SIZE * 3];
    
    //
    //set up the first update VAO
    //  
    
    //initialize flock positions
    init_particles(positions, FLOCK_SIZE, FLOCK_SCALE, FLOCK_CENTER); 
    
    //set up position stuff
    glBindVertexArray(update_vao[0]);
    glBindBuffer(GL_ARRAY_BUFFER, particle_position[0]);
    glBufferData(GL_ARRAY_BUFFER, FLOCK_SIZE * 3 * sizeof(float), positions, GL_DYNAMIC_COPY);
    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, NULL);
    glEnableVertexAttribArray(0);
    
    //initialize velocities
    init_particles(positions, FLOCK_SIZE, MAX_INITIAL_FLOCK_SPEED, NULL);
    glBindBuffer(GL_ARRAY_BUFFER, particle_velocity[0]);
    glBufferData(GL_ARRAY_BUFFER, FLOCK_SIZE * 3 * sizeof(float), positions, GL_DYNAMIC_COPY);
    glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 0, NULL);
    glEnableVertexAttribArray(1);
    
    //set up original colors
    init_particles(positions, FLOCK_SIZE, 1.0f, NULL);
    glBindBuffer(GL_ARRAY_BUFFER, particle_color[0]);
    glBufferData(GL_ARRAY_BUFFER, FLOCK_SIZE * 3 * sizeof(float), positions, GL_DYNAMIC_COPY);
    glVertexAttribPointer(2, 3, GL_FLOAT, GL_FALSE, 0, NULL);
    glEnableVertexAttribArray(2);
    
    //set up original color velocities
    init_particles(positions, FLOCK_SIZE, 1.0f, NULL);
    glBindBuffer(GL_ARRAY_BUFFER, particle_colorVelocity[0]);
    glBufferData(GL_ARRAY_BUFFER, FLOCK_SIZE * 3 * sizeof(float), positions, GL_DYNAMIC_COPY);
    glVertexAttribPointer(3, 3, GL_FLOAT, GL_FALSE, 0, NULL);
    glEnableVertexAttribArray(3);
    
    //everything is now on the GPU. Crazy. Delete the data from memory.
    delete[] positions;
}

void translateToOPGL(float goal_position[])
{
    M3DMatrix44f worldView, window_to_world, device_to_window;
    M3DMatrix44f objectToScreen;
 
    const float* projection = view_frustum.GetProjectionMatrix();
        
    M3DVector4f screenCoords = {flockingX, flockingY, 0.0f, 1.0f};
    
    view_frame.GetCameraMatrix(worldView, false);
        
    device_to_window[0] = WINDOW_WIDTH/2;
    device_to_window[1] = 0.0f;
    device_to_window[2] = 0.0f; 
    device_to_window[3] = 0.0f;
    device_to_window[4] = 0.0f;
    device_to_window[5] = WINDOW_HEIGHT/2;
    device_to_window[6] = 0.0f;
    device_to_window[7] = 0.0f;
    device_to_window[8] = 0.0f;
    device_to_window[9] = 0.0f;
    device_to_window[10] = -0.5f;
    device_to_window[11] = 0.0f;
    device_to_window[12] = WINDOW_WIDTH/2;
    device_to_window[13] = WINDOW_HEIGHT/2;
    device_to_window[14] = 0.5f;
    device_to_window[15] = 1.0f;
        
    m3dMatrixMultiply44(objectToScreen, projection, worldView);
    
    m3dMatrixMultiply44(device_to_window,  device_to_window, objectToScreen);
    
    m3dInvertMatrix44(window_to_world, device_to_window);
    
    M3DVector4f worldCoords;
    m3dTransformVector4(worldCoords, screenCoords, window_to_world);    
    
    //printf("%f %f %f %f\n", worldCoords[0], worldCoords[1], worldCoords[2], worldCoords[3]);
    
    // World coords found, setting goal positions
    // Need to get the ratio for the depth in world space (-1.0 - world coords)
    // worldcoords[0/1]/worldCoords[3] is to homogenize because worldcoords[3]
    // Should be 1.0
    goal_position[0] = (-worldCoords[0]/worldCoords[3])*(-1.0-worldCoords[2]);
    goal_position[1] = (-worldCoords[1]/worldCoords[3])*(-1.0-worldCoords[2]);
    goal_position[2] = 0.0f;    
    
    //printf("%f %f %f\n", goal_position[0], goal_position[1], worldCoords[2]);    
}

void getMouseCoords(int x, int y){
 
    flockingX = (float)x;
    flockingY = (float)y;
    
    glutPostRedisplay();
}

// default idle flocking goal
void update_idle_flocking(float goal_position[], float goal_color[], float time){
    //x, y, and z are set based on sin an cos functions
/*  goal_position[0] = 240.0f * sinf(time * 1.0f * 3.141592f);
    goal_position[1] = 240.0f * cosf(time * 3.0f * 3.141592f);
    goal_position[2] = 240.0f * cosf(time * 2.5f * 3.141592f);
*/
    // Sets the height and width of the window 
    WINDOW_HEIGHT = glutGet(GLUT_WINDOW_HEIGHT);
    WINDOW_WIDTH = glutGet(GLUT_WINDOW_WIDTH);
    
    glutPassiveMotionFunc(getMouseCoords);
    translateToOPGL(goal_position);
    
    /*
    goal_color[0] = 2.0f * sinf(time * 0.05f * 3.141592f);
    goal_color[1] = 2.0f * cosf(time * 0.069f * 3.141592f);
    goal_color[2] = 2.0f * cosf(time * 0.042f * 3.141592f);
    */
    int color = (int)(time / 4.0f);
    color = color % 9;
    
    if (color == 0){
	//goal is pink!
	goal_color[0] = 1.0f;
	goal_color[1] = 0.25f;
	goal_color[2] = 0.5f;
    }
    else if (color == 1){
	//goal is red!
	goal_color[0] = 1.0f;
	goal_color[1] = 0.0f;
	goal_color[2] = 0.0f;
    }
    else if (color == 2){
	//goal is orange!
	goal_color[0] = 1.0f;
	goal_color[1] = 0.7f;
	goal_color[2] = 0.2f;
    }
    else if (color == 3){
	//goal is yellow!
	goal_color[0] = 1.0f;
	goal_color[1] = 1.0f;
	goal_color[2] = 0.2f;
    }
    else if (color == 4){
	//goal is green!
	goal_color[0] = 0.3f;
	goal_color[1] = 1.0f;
	goal_color[2] = 0.0f;
    }
    else if (color == 5){
	//goal is cyan!
	goal_color[0] = 0.0f;
	goal_color[1] = 1.0f;
	goal_color[2] = 1.0f;
    }
    else if (color == 6){
	//goal is blue!
	goal_color[0] = 0.0f;
	goal_color[1] = 0.0f;
	goal_color[2] = 1.0f;
    }
    else if (color == 7){
	//goal is violet!
	goal_color[0] = 0.5f;
	goal_color[1] = 0.1f;
	goal_color[2] = 0.9f;
    }
    else if (color == 8){
	//goal is white!
	goal_color[0] = 1.0f;
	goal_color[1] = 1.0f;
	goal_color[2] = 1.0f;
    }
}